package cn.xuqiudong.rpc.protocol;

import cn.xuqiudong.rpc.common.model.Invoker;
import cn.xuqiudong.rpc.common.model.XqdResponse;
import cn.xuqiudong.rpc.common.model.XqdUrl;
import cn.xuqiudong.rpc.protocol.netty.XqdNettyClient;
import cn.xuqiudong.rpc.protocol.netty.handler.XqdNettyClientHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.DefaultEventLoop;
import io.netty.util.concurrent.DefaultPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.xuqiudong.rpc.cluster.ClusterFacade;
import cn.xuqiudong.rpc.protocol.netty.RequestHolder;
import cn.xuqiudong.rpc.protocol.netty.XqdNettyServer;
import cn.xuqiudong.rpc.protocol.netty.model.NettyProtocolData;
import cn.xuqiudong.rpc.protocol.netty.model.XqdFuture;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述: 基于netty的通讯
 * @author Vic.xu
 * @date 2022-02-23 17:31
 */
public class XqdProtocol implements Protocol {

    private static Logger logger = LoggerFactory.getLogger(XqdProtocol.class);
    /**
     * 用于复用client , key =  providerUrl
     */
    private static Map<String, XqdNettyClient>  clientMap= new ConcurrentHashMap<>();


    @Override
    public void start(int port) {
        new XqdNettyServer(port).start();
    }

    @Override
    public Object send(Invoker invoker) throws Exception {
        XqdUrl xqdUrl = ClusterFacade.fetchUrlLoadBalance(invoker);
        //把请求地址设置到invoker 上下文
        invoker.setXqdUrl(xqdUrl);
        XqdNettyClient client = getClient(xqdUrl);

        //设置当前请求标识
        long requestId = RequestHolder.getRequestId();
        invoker.setRequestId(requestId);

        //FIXME  此处暂时统一 异步转同步，  后续考虑  根据配置动态决定 是否转同步 还是使用异步
        XqdFuture<XqdResponse> future = new XqdFuture<>(new DefaultPromise<>(new DefaultEventLoop()));

        /**
          *  把当前future 以及对应的requestId存入RequestHolder，
          *  在netty返回消息后{@link XqdNettyClientHandler#channelRead0(ChannelHandlerContext, NettyProtocolData)}
          *  通知其操作成功
         */
        RequestHolder.REQUEST_MAP.put(requestId, future);

        client.sendRequest(invoker);
        XqdResponse response = future.getPromise().get();
        return response.getResultData();

    }

    /**
     * 获取可用的nettyClient
     * @param xqdUrl
     * @return
     */
    private XqdNettyClient getClient(XqdUrl xqdUrl){
        String address = xqdUrl.getAddress();
        XqdNettyClient client = clientMap.computeIfAbsent(address, key->new XqdNettyClient());
        return client;
    }

}
